package base

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

// ImportMode 导入方式
type ImportMode string

const (
	//InsertAndUpdate 有则更新、无则插入
	InsertAndUpdate ImportMode = "InsertAndUpdate"
	//TruncteAfterInsert 先清空表再插入
	TruncteAfterInsert ImportMode = "TruncteAfterInsert"
	//DeleteAfterInsert 先按照参数删除再插入
	DeleteAfterInsert ImportMode = "DeleteAfterInsert"
	//InsertOnly 仅插入
	InsertOnly ImportMode = "InsertOnly"
	//UpdateOnly 仅更新
	UpdateOnly ImportMode = "UpdateOnly"
)

func (i ImportMode) String() string {
	switch i {
	case InsertAndUpdate:
		return "InsertAndUpdate"
	case TruncteAfterInsert:
		return "TruncteAfterInsert"
	case DeleteAfterInsert:
		return "DeleteAfterInsert"
	case InsertOnly:
		return "InsertOnly"
	case UpdateOnly:
		return "UpdateOnly"
	default:
		return "ERROR"
	}
}

// DataMap 数据映射
type DataMap struct {
	DestColName string
	SrcColIndex int
	SrcColName  string
	IsRequest   bool
	Convert     *DataConvert
}

// DataConvert 数据转换
type DataConvert struct {
	DaoName     string
	IdColName   string
	TextColName string
	Multiple    bool
}

// ErrorInfo 错误信息
type ErrorInfo struct {
	ColIndex int
	Message  string
}

// ErrorData 错误数据
type ErrorData struct {
	RowIndex   int
	DataValues []string
	Message    string
	ErrorInfos []ErrorInfo
}

// ImportConfig 导入配置
type ImportConfig struct {
	Name   string `gorm:"primary_key;column:name;size:50"`
	Config string `gorm:"column:config;"`
}

// CallBackFunc 导入过程中触发的回掉函数
type CallBackFunc func(*gorm.DB, map[string]string) error

// ValidationCallBackFunc 验证一行数据时触发的回掉函数
type ValidationCallBackFunc func(int, []string) []ErrorData

// TableName 导入配置对应的表名
func (ImportConfig) TableName() string {
	return "T_CONF_IMPORT"
}

// DataReader 数据读取接口
type DataReader interface {
	GetSheetNames() ([]string, error)
	GetDataColumns(string) ([]string, error)
	ReadData(string) ([][]string, []int, error)
}

// DataImport 数据导入结构体
type DataImport struct {
	ImportName    string
	DaoName       string
	FilePath      string
	SheetName     string
	Params        map[string]string
	Keys          []string
	Mode          ImportMode
	HasColHeader  bool
	ForbidConvert bool
	SaveByDao     bool
	PermId        int
	reader        DataReader
	dao           Dao
	paramsConvMap map[string]ConvertFunc
	paramIndex    map[string]int
	paramVals     []interface{}
	gormSchema    *schema.Schema
}

// NewDataImport 创建数据导入对象
func NewDataImport(importName, daoName, filePath,
	sheetName string, params map[string]string, keys []string,
	hasColHeader bool, mode ImportMode) (*DataImport, error) {
	di := &DataImport{
		ImportName:   importName,
		DaoName:      daoName,
		FilePath:     filePath,
		SheetName:    sheetName,
		Params:       params,
		Keys:         keys,
		Mode:         mode,
		HasColHeader: hasColHeader,
	}
	err := di.InitReader()
	if err != nil {
		return nil, err
	}
	return di, nil
}

// InitReader 初始化数据读取
func (d *DataImport) InitReader() error {
	if d.FilePath == "" {
		return errors.New("FilePath不能为空！")
	}
	filePath := d.FilePath
	_, eName := GetExtenName(filePath)
	eName = strings.ToUpper(eName)
	if eName == "XLSX" {
		reader, err := NewXlsxDataReader(filePath, true)
		if err != nil {
			return err
		}
		d.reader = reader
	} else {
		return errors.New("数据导入只支持xlsx文件！")
	}
	return nil
}

// GetDataColumns 获取导入文件的数据列
func (d *DataImport) GetDataColumns() ([]string, error) {
	return d.reader.GetDataColumns(d.SheetName)
}

// GetSheetNames 获取导入文件的所有表单
func (d *DataImport) GetSheetNames() ([]string, error) {
	return d.reader.GetSheetNames()
}

// GetDataMap 获取数据映射
func (d *DataImport) GetDataMap() ([]DataMap, error) {
	var configs []ImportConfig
	err := MYDB.Where("name=?", d.ImportName).Find(&configs).Error
	if err != nil {
		return nil, err
	}
	if l := len(configs); l == 0 {
		errInfo := fmt.Sprintf("名称为%s的导入配置不存在！", d.ImportName)
		return nil, errors.New(errInfo)
	}
	config := configs[0]
	bytes := []byte(config.Config)
	var dataMap []DataMap
	err = json.Unmarshal(bytes, &dataMap)
	if err != nil {
		return nil, err
	}
	return dataMap, nil
}

// SetDataMap 保存数据映射
func (d *DataImport) SetDataMap(dataMap []DataMap, isNew bool) error {
	var count int64
	err := MYDB.Model(&ImportConfig{}).Where("name=?", d.ImportName).Count(&count).Error
	if err != nil {
		return err
	}
	errInfo := ""
	if isNew && count > 0 {
		errInfo = fmt.Sprintf("已经存在名称为%s的导入配置！", d.ImportName)
		return errors.New(errInfo)
	}
	if !isNew && count == 0 {
		errInfo = fmt.Sprintf("名称为%s的导入配置不存在！", d.ImportName)
		return errors.New(errInfo)
	}
	bytes, err := json.Marshal(dataMap)
	if err != nil {
		return err
	}
	str := string(bytes)
	config := &ImportConfig{
		Name:   d.ImportName,
		Config: str,
	}
	if isNew {
		err = MYDB.Create(&config).Error
	} else {
		err = MYDB.Save(&config).Error
	}
	if err != nil {
		return err
	}
	return nil
}

// Import 导入数据
func (d *DataImport) Import(fileName string, ignoreErr bool, correctDatas []ErrorData) {
	dao, err := GetDao(d.DaoName)
	infoMap := make(map[string]interface{})
	key := "ip:" + fileName
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		return
	}
	d.dao = dao
	d.gormSchema, err = schema.Parse(dao.GetEntity(), AppConf.cacheSchema, schema.NamingStrategy{})
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		return
	}
	d.paramsConvMap = make(map[string]ConvertFunc)
	var field *schema.Field
	for k := range d.Params {
		for _, f := range d.gormSchema.Fields {
			if strings.EqualFold(f.Name, k) {
				field = f
				break
			}
		}
		if field != nil {
			conv, _, _ := d.getConvertFunc(field.DataType)
			if conv != nil {
				d.paramsConvMap[k] = conv
			}
		}
	}
	if d.SaveByDao {
		d.importByDao(fileName, ignoreErr, correctDatas)
	} else {
		d.importBySQL(fileName, ignoreErr, correctDatas)
	}
}

// importBySQL 通过SQL导入数据
func (d *DataImport) importBySQL(fileName string, ignoreErr bool, correctDatas []ErrorData) {
	bcbName := d.ImportName + "-before"
	callCack := callBackPool[bcbName]
	key := "ip:" + fileName
	infoMap := make(map[string]interface{})
	infoMap["msg"] = "导入前的初始化"
	infoMap["finish"] = 0
	infoMap["percent"] = 1
	infoMap["success"] = 0
	infoMap["dataLen"] = 0
	infoMap["err"] = ""
	tx := MYDB.Begin()
	if callCack != nil {
		err := callCack(tx, d.Params)
		if err != nil {
			tx.Rollback()
			infoMap["err"] = strings.Replace(err.Error(), "\"", "", -1)
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	RedisCli.HMSet(rctx, key, infoMap)
	dataMap, err := d.GetDataMap()
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	infoMap["msg"] = "读取并解析数据"
	infoMap["finish"] = 0
	infoMap["percent"] = 5
	RedisCli.HMSet(rctx, key, infoMap)
	okDatas, errDatas, err := d.parseData(dataMap, d.dao, correctDatas)
	errBuff := make([]string, 0, 11)
	infoMap["dataLen"] = len(okDatas)
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	if l := len(errDatas); !ignoreErr && l > 0 {
		infoMap["finish"] = 1
		i := 0
		for _, errData := range errDatas {
			for _, info := range errData.ErrorInfos {
				errInfo := fmt.Sprintf("在%d行%d列有错误:%s", errData.RowIndex+2, info.ColIndex+1, info.Message)
				errBuff = append(errBuff, errInfo)
				i++
			}
			if i > 10 {
				errBuff = append(errBuff, "错误太多了...")
				break
			}
		}
		infoMap["err"] = strings.Join(errBuff, "|")
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	infoMap["msg"] = "导入数据到系统"
	infoMap["finish"] = 0
	infoMap["percent"] = 7
	RedisCli.HMSet(rctx, key, infoMap)
	insertSql, updateSql, deleteSql, countSql, err := d.getSql(dataMap)
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	RedisCli.HMSet(rctx, key, infoMap)
	if d.Mode == DeleteAfterInsert {
		if err := tx.Exec(deleteSql, d.paramVals...).Error; err != nil {
			tx.Rollback()
			infoMap["err"] = err.Error()
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	} else if d.Mode == TruncteAfterInsert {
		tSql := fmt.Sprintf(`truncate table "%s"`, d.gormSchema.Table)
		if err := tx.Exec(tSql).Error; err != nil {
			tx.Rollback()
			infoMap["err"] = err.Error()
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	RedisCli.HMSet(rctx, key, infoMap)
	isInsert := true
	if d.Mode == InsertAndUpdate {
		isInsert = false
	}
	var dbErr error
	dLen := len(okDatas)
	for i, rowData := range okDatas {
		if i%1000 == 0 {
			infoMap["percent"] = i * 100 / dLen
			RedisCli.HMSet(rctx, key, infoMap)
		}
		if !isInsert {
			ok, keyValues, err := d.isUpdate(dataMap, rowData, countSql)
			if err != nil {
				tx.Rollback()
				infoMap["err"] = err.Error()
				infoMap["finish"] = 1
				RedisCli.HMSet(rctx, key, infoMap)
				return
			}
			if ok {
				pLen := len(rowData) + len(keyValues)
				params := make([]interface{}, 0, pLen)
				params = append(params, rowData...)
				params = append(params, keyValues...)
				dbErr = tx.Exec(updateSql, params...).Error
			} else {
				if d.Mode != UpdateOnly {
					isInsert = true
				}
			}
		}
		if isInsert {
			dbErr = tx.Exec(insertSql, rowData...).Error
		}
		if dbErr != nil {
			tx.Rollback()
			infoMap["err"] = dbErr.Error()
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	bcbName = d.ImportName + "-after"
	callCack = callBackPool[bcbName]
	infoMap["msg"] = "处理导入到系统中的数据"
	infoMap["finish"] = 0
	infoMap["percent"] = 90
	RedisCli.HMSet(rctx, key, infoMap)
	if callCack != nil {
		err := callCack(tx, d.Params)
		if err != nil {
			tx.Rollback()
			infoMap["err"] = strings.Replace(err.Error(), "\"", "", -1)
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	tx.Commit()
	infoMap["percent"] = 100
	infoMap["finish"] = 1
	infoMap["success"] = 1
	RedisCli.HMSet(rctx, key, infoMap)
}

// importByDao 通过Dao导入数据
func (d *DataImport) importByDao(fileName string, ignoreErr bool, correctDatas []ErrorData) {
	bcbName := d.ImportName + "-before"
	callCack := callBackPool[bcbName]
	key := "ip:" + fileName
	infoMap := make(map[string]interface{})
	infoMap["msg"] = "导入前的初始化"
	infoMap["finish"] = 0
	infoMap["percent"] = 1
	infoMap["success"] = 0
	infoMap["dataLen"] = 0
	infoMap["err"] = ""
	tx := MYDB.Begin()
	if callCack != nil {
		err := callCack(tx, d.Params)
		if err != nil {
			tx.Rollback()
			infoMap["err"] = strings.Replace(err.Error(), "\"", "", -1)
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	RedisCli.HMSet(rctx, key, infoMap)
	dataMap, err := d.GetDataMap()
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	infoMap["msg"] = "读取并解析数据"
	infoMap["finish"] = 0
	infoMap["percent"] = 5
	RedisCli.HMSet(rctx, key, infoMap)
	okDatas, errDatas, err := d.parseData(dataMap, d.dao, correctDatas)
	errBuff := make([]string, 0, 11)
	infoMap["dataLen"] = len(okDatas)
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	if l := len(errDatas); !ignoreErr && l > 0 {
		infoMap["finish"] = 1
		i := 0
		for _, errData := range errDatas {
			for _, info := range errData.ErrorInfos {
				errInfo := fmt.Sprintf("在%d行%d列有错误:%s", errData.RowIndex+2, info.ColIndex+1, info.Message)
				errBuff = append(errBuff, errInfo)
				i++
			}
			if i > 10 {
				errBuff = append(errBuff, "错误太多了...")
				break
			}
		}
		infoMap["err"] = strings.Join(errBuff, "|")
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	infoMap["msg"] = "导入数据到系统"
	infoMap["finish"] = 0
	infoMap["percent"] = 7
	RedisCli.HMSet(rctx, key, infoMap)
	_, _, deleteSql, _, err := d.getSql(dataMap)
	if err != nil {
		infoMap["err"] = err.Error()
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		tx.Rollback()
		return
	}
	RedisCli.HMSet(rctx, key, infoMap)
	if d.Mode == DeleteAfterInsert {
		if err := tx.Exec(deleteSql, d.paramVals...).Error; err != nil {
			tx.Rollback()
			infoMap["err"] = err.Error()
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	} else if d.Mode == TruncteAfterInsert {
		tSql := fmt.Sprintf(`truncate table "%s"`, d.gormSchema.Table)
		if err := tx.Exec(tSql).Error; err != nil {
			tx.Rollback()
			infoMap["err"] = err.Error()
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	RedisCli.HMSet(rctx, key, infoMap)
	dLen := len(okDatas)
	for i, rowData := range okDatas {
		if i%1000 == 0 {
			infoMap["percent"] = i * 100 / dLen
			RedisCli.HMSet(rctx, key, infoMap)
		}
		entity, err := d.assignEntity(dataMap, d.dao, rowData, tx)
		if err != nil {
			errBuff = append(errBuff, fmt.Sprintf("在%d行有错误:%s", i+2, err.Error()))
		} else {
			err = tx.Save(entity).Error
			if err != nil {
				errBuff = append(errBuff, fmt.Sprintf("在%d行有错误:%s", i+2, err.Error()))
			}
		}
		if len(errBuff) > 10 {
			errBuff = append(errBuff, "错误太多了...")
			break
		}
	}
	if len(errBuff) > 0 {
		tx.Rollback()
		infoMap["err"] = strings.Join(errBuff, "|")
		infoMap["finish"] = 1
		RedisCli.HMSet(rctx, key, infoMap)
		return
	}
	bcbName = d.ImportName + "-after"
	callCack = callBackPool[bcbName]
	infoMap["msg"] = "处理导入到系统中的数据"
	infoMap["finish"] = 0
	infoMap["percent"] = 90
	RedisCli.HMSet(rctx, key, infoMap)
	if callCack != nil {
		err := callCack(tx, d.Params)
		if err != nil {
			tx.Rollback()
			infoMap["err"] = strings.Replace(err.Error(), "\"", "", -1)
			infoMap["finish"] = 1
			RedisCli.HMSet(rctx, key, infoMap)
			return
		}
	}
	tx.Commit()
	infoMap["percent"] = 100
	infoMap["finish"] = 1
	infoMap["success"] = 1
	RedisCli.HMSet(rctx, key, infoMap)
}

func (d *DataImport) assignEntity(dataMap []DataMap, dao Dao, rowData []interface{}, tx *gorm.DB) (entity interface{}, err error) {
	var val interface{}
	entity = dao.GetEntity()
	if d.Mode == InsertAndUpdate || d.Mode == UpdateOnly {
		keyVals, err := d.getKeyValues(dataMap, rowData)
		if err != nil {
			return nil, err
		}
		kvMap := make(map[string]interface{}, len(d.Keys))
		for i, key := range d.Keys {
			fname := LookUpFieldName(d.gormSchema, key)
			kvMap[fname] = keyVals[i]
		}
		tx.Where(kvMap).First(entity)
	}
	for _, f := range d.gormSchema.Fields {
		var dbfName string
		for i, item := range dataMap {
			if item.SrcColIndex == -2 {
				continue
			}
			if strings.EqualFold(f.Name, item.DestColName) {
				dbfName = f.Name
				val = rowData[i]
				break
			}
		}
		if dbfName == "" {
			for k, v := range d.Params {
				if strings.EqualFold(f.Name, k) {
					dbfName = f.Name
					val, _, _ = d.paramsConvMap[k](0, 0, v)
					break
				}
			}
		}
		if dbfName == "" {
			continue
		}
		reflectV := reflect.ValueOf(entity)
		if err = f.Set(reflectV, val); err != nil {
			return
		}
	}
	return
}

// isUpdate 判断记录是否存在
func (d *DataImport) isUpdate(dataMap []DataMap, rowData []interface{}, countSql string) (bool, []interface{}, error) {
	keyValues, err := d.getKeyValues(dataMap, rowData)
	if err != nil {
		return false, nil, err
	}
	rows, err := MYDB.Raw(countSql, keyValues...).Rows()
	if err != nil {
		return false, nil, err
	}
	defer rows.Close()
	var val int
	if rows.Next() {
		rows.Scan(&val)
	}
	if val > 0 {
		return true, keyValues, nil
	}
	return false, nil, nil
}
func (d *DataImport) getKeyValues(dataMap []DataMap, rowData []interface{}) (keyVals []interface{}, err error) {
	klen := len(d.Keys)
	keyVals = make([]interface{}, klen)
	for ki, key := range d.Keys {
		ok := false
		for di, m := range dataMap {
			if strings.EqualFold(m.DestColName, key) {
				keyVals[ki] = rowData[di]
				ok = true
				continue
			}
		}
		if !ok {
			for k, v := range d.Params {
				if strings.EqualFold(k, key) {
					keyVals[ki] = v
					ok = true
					continue
				}
			}
		}
		if !ok {
			err = fmt.Errorf("字段[%s]在结构体[%s]中不存在！", key, d.DaoName)
			return
		}
	}
	return
}
func (d *DataImport) getSql(dataMap []DataMap) (insertSql string, updateSql string, deleteSql string, countSql string, err error) {
	tName := d.gormSchema.Table
	var insertBuff, updateBuff, deleteBuff, countBuff bytes.Buffer
	insertBuff.WriteString("insert into \"")
	insertBuff.WriteString(tName)
	insertBuff.WriteString("\" (")
	updateBuff.WriteString("update \"")
	updateBuff.WriteString(tName)
	updateBuff.WriteString("\" set ")
	var dbfName string
	ignoreCount := 0
	for _, item := range dataMap {
		if item.SrcColIndex == -2 {
			ignoreCount++
			continue
		}
		dbfName = LookUpFieldName(d.gormSchema, item.DestColName)
		insertBuff.WriteString("\"")
		insertBuff.WriteString(dbfName)
		insertBuff.WriteString("\",")
		updateBuff.WriteString("\"")
		updateBuff.WriteString(dbfName)
		updateBuff.WriteString("\"=?,")
	}
	insertBuff.Truncate(insertBuff.Len() - 1)
	insertBuff.WriteString(") values (")
	mapLen := len(dataMap) - ignoreCount
	insertBuff.WriteString(strings.Repeat("?,", mapLen))
	insertBuff.Truncate(insertBuff.Len() - 1)
	insertBuff.WriteString(")")
	updateBuff.Truncate(updateBuff.Len() - 1)
	updateBuff.WriteString(" where ")
	countBuff.WriteString("select count(*) from \"")
	countBuff.WriteString(tName)
	countBuff.WriteString("\" where ")
	keyLen := len(d.Keys)
	for k, key := range d.Keys {
		dbfName = LookUpFieldName(d.gormSchema, key)
		updateBuff.WriteString("\"")
		updateBuff.WriteString(dbfName)
		updateBuff.WriteString("\"=?")
		countBuff.WriteString("\"")
		countBuff.WriteString(dbfName)
		countBuff.WriteString("\"=?")
		if k < keyLen-1 {
			updateBuff.WriteString(" AND ")
			countBuff.WriteString(" AND ")
		}
	}
	deleteBuff.WriteString("delete from \"")
	deleteBuff.WriteString(tName)
	deleteBuff.WriteString("\" where ")
	pLen := len(d.Params)
	pi := 0
	pVals := make([]interface{}, 0, 5)
	var pval interface{}
	for key, val := range d.Params {
		dbfName = LookUpFieldName(d.gormSchema, key)
		deleteBuff.WriteString("\"")
		deleteBuff.WriteString(dbfName)
		deleteBuff.WriteString("\"=?")
		if pi < pLen-1 {
			deleteBuff.WriteString(" AND ")
		}
		pval = val
		// if field.DataType == schema.Time {
		// 	var rq time.Time
		// 	rq, err = time.Parse(time.RFC3339, val)
		// 	pval = rq.Format("2006-01-02")
		// 	if err != nil {
		// 		return
		// 	}
		// }
		pVals = append(pVals, pval)
		pi++
	}
	d.paramVals = pVals
	countSql = countBuff.String()
	insertSql = insertBuff.String()
	updateSql = updateBuff.String()
	deleteSql = deleteBuff.String()
	return
}
func (d *DataImport) parseData(dataMap []DataMap, dao Dao, correctDatas []ErrorData) ([][]interface{}, []ErrorData, error) {
	datas, bRowIndexs, err := d.reader.ReadData(d.SheetName)
	if err != nil {
		return nil, nil, err
	}
	for _, cData := range correctDatas {
		if l := len(cData.ErrorInfos); l > 0 {
			continue
		}
		datas[cData.RowIndex] = cData.DataValues
	}
	mapLen := len(dataMap)
	converts := make([]ConvertFunc, mapLen)
	sizes := make([]int, mapLen)
	indexParamVals := make(map[int]string, len(d.Params))
	paramIndex := make(map[string]int, len(d.Params))
	ignoreCount := 0
	for i, item := range dataMap {
		if item.SrcColIndex == -2 {
			ignoreCount++
			continue
		}
		if strings.HasPrefix(item.SrcColName, "@") {
			pName := item.SrcColName[1:]
			indexParamVals[i] = d.Params[pName]
			paramIndex[item.SrcColName] = i
		}
		var field *schema.Field
		for _, f := range d.gormSchema.Fields {
			if strings.EqualFold(f.Name, item.DestColName) {
				field = f
				break
			}
		}
		if field == nil {
			errInfo := fmt.Sprintf("配置错误，实体[%s]中没有找到字段[%s]", d.DaoName, item.DestColName)
			return nil, nil, errors.New(errInfo)
		}
		ty := field.DataType
		if ty == "" {
			ty = field.GORMDataType
		}
		convert, size, err := d.getConvertFunc(ty)
		if err != nil {
			return nil, nil, err
		}
		converts[i] = convert
		sizes[i] = size
	}
	d.paramIndex = paramIndex
	mapLen = mapLen - ignoreCount
	dataLen := len(datas)
	okDatas := make([][]interface{}, 0, dataLen)
	errDatas := make([]ErrorData, 0, 50)
	for i, rowData := range datas {
		isBankRow := false
		for _, index := range bRowIndexs {
			if index == i {
				isBankRow = true
			}
		}
		if isBankRow {
			continue
		}
		errInfos := make([]ErrorInfo, 0, 5)
		okData := make([]interface{}, mapLen)
		k := 0
		pLen := 0
		for j, item := range dataMap {
			if item.SrcColIndex == -2 {
				continue
			}
			if item.SrcColIndex == -1 {
				pLen++
			}
			var cellData string
			if indexParamVals[j] != "" {
				cellData = indexParamVals[j]
			} else {
				if item.SrcColIndex < 0 || item.SrcColIndex > len(rowData)-1 {
					return nil, nil, errors.New("导入模板错误请检查！")
				}
				cellData = rowData[item.SrcColIndex]
			}
			if item.IsRequest && cellData == "" {
				errInfo := ErrorInfo{
					ColIndex: k - pLen,
					Message:  "不允许为空！",
				}
				errInfos = append(errInfos, errInfo)
			}
			cellData = strings.TrimSpace(cellData)
			cellData = strings.Replace(cellData, "\t", "", -1)
			cellData = strings.Replace(cellData, "\r", "", -1)
			cellData = strings.Replace(cellData, "\n", "", -1)
			if item.SrcColIndex >= 0 && item.Convert != nil && item.Convert.DaoName != "" && !d.ForbidConvert {
				data, err := ConvertData(item.Convert, cellData, item.IsRequest)
				if err != nil {
					errInfo := ErrorInfo{
						ColIndex: k - pLen,
						Message:  err.Error(),
					}
					errInfos = append(errInfos, errInfo)
				} else {
					okData[k] = data
				}
			} else {
				data, ok, errInfo := converts[j](k, sizes[j], cellData)
				if ok {
					okData[k] = data
				} else {
					errInfos = append(errInfos, *errInfo)
				}
			}
			k++
		}
		cb := validationCallBackPool[d.ImportName]
		if cb != nil {
			eds := cb(i, rowData)
			if eds != nil {
				errDatas = append(errDatas, eds...)
			}
		}
		if l := len(errInfos); l > 0 {
			errData := ErrorData{
				RowIndex:   i,
				DataValues: rowData,
				ErrorInfos: errInfos,
			}
			errDatas = append(errDatas, errData)
		} else {
			okDatas = append(okDatas, okData)
		}
	}
	return okDatas, errDatas, nil
}

// ConvertData 根据DataConvert转换数据
func ConvertData(conv *DataConvert, text string, request bool) (interface{}, error) {
	if conv.DaoName == "#fixed" {
		ids := strings.Split(conv.IdColName, ":")
		texts := strings.Split(conv.TextColName, ":")
		l1 := len(ids)
		l2 := len(texts)
		errInfo := ""
		if l1 != l2 {
			errInfo = fmt.Sprintf("值只能是[%s]中的一项", conv.TextColName)
		}
		index := -1
		for i, t := range texts {
			if t == text {
				index = i
			}
		}
		if index == -1 {
			errInfo = fmt.Sprintf("固定值列表配置错误，[%s]在TEXT列表中没有找到！", text)
		}
		if errInfo == "" {
			return ids[index], nil
		}
		return nil, errors.New(errInfo)
	}
	cDao, err := GetDao(conv.DaoName)
	if err != nil {
		return nil, err
	}
	gs, err := schema.Parse(cDao.GetEntity(), AppConf.cacheSchema, schema.NamingStrategy{})
	if err != nil {
		return nil, err
	}
	tName := gs.Table
	idDBName := LookUpFieldName(gs, conv.IdColName)
	textDBName := LookUpFieldName(gs, conv.TextColName)
	var retVals []string
	if request || text != "" {
		sql := fmt.Sprintf(`select "%s" from "%s" where "%s" in (?)`, idDBName, tName, textDBName)
		var vals interface{} = text
		if conv.Multiple {
			text = strings.Replace(text, "，", ",", -1)
			vals = strings.Split(text, ",")
		}
		rows, err := MYDB.Raw(sql, vals).Rows()
		if err != nil {
			return nil, err
		}
		defer rows.Close()
		for rows.Next() {
			var val string
			err = rows.Scan(&val)
			if err != nil {
				return nil, err
			}
			retVals = append(retVals, val)
		}
	} else {
		retVals = []string{""}
	}
	if l := len(retVals); l == 0 {
		errInfo := fmt.Sprintf("在系统中没有找到匹配的名称：%s！", text)
		return nil, errors.New(errInfo)
	} else if l == 1 {
		return retVals[0], nil
	} else {
		return strings.Join(retVals, ","), nil
	}
}

// ConvertFunc 数据转换函数
type ConvertFunc func(colIndex int, size int, strData string) (interface{}, bool, *ErrorInfo)

// Convert2Int 转换数据为Int
func Convert2Int(colIndex int, size int, strData string) (interface{}, bool, *ErrorInfo) {
	strData = strings.Trim(strData, " ")
	if strData == "" {
		return nil, true, nil
	}
	data, err := strconv.Atoi(strData)
	if err == nil {
		return data, true, nil
	} else {
		errInfo := &ErrorInfo{
			ColIndex: colIndex,
			Message:  fmt.Sprintf("%s不是有效的整数！", strData),
		}
		return nil, false, errInfo
	}
}

// Convert2Float 转换数据为Float
func Convert2Float(colIndex int, size int, strData string) (interface{}, bool, *ErrorInfo) {
	strData = strings.Trim(strData, " ")
	if strData == "" {
		return nil, true, nil
	}
	percent := false
	if strings.HasSuffix(strData, "%") {
		percent = true
		strData = strings.Trim(strData, "%")
	}
	data, err := strconv.ParseFloat(strData, 64)
	if err == nil {
		if percent {
			data = data / 100
		}
		return data, true, nil
	} else {
		errInfo := &ErrorInfo{
			ColIndex: colIndex,
			Message:  fmt.Sprintf("%s不是有效的数值！", strData),
		}
		return nil, false, errInfo
	}
}

// Convert2Bool 转换数据为Bool
func Convert2Bool(colIndex int, size int, strData string) (interface{}, bool, *ErrorInfo) {
	strData = strings.Trim(strData, " ")
	strData = strings.ToLower(strData)
	data := false
	if strData == "是" || strData == "t" || strData == "1" || strData == "true" {
		data = true
	}
	return data, true, nil
}

// Convert2String 转换数据为字符串
func Convert2String(colIndex int, size int, strData string) (interface{}, bool, *ErrorInfo) {
	strData = strings.Trim(strData, " ")
	if l := len(strData); size > 0 && l > size {
		errInfo := &ErrorInfo{
			ColIndex: colIndex,
			Message:  fmt.Sprintf("%s长度不能超过%d！", strData, size),
		}
		return nil, false, errInfo
	}
	return strData, true, nil
}

// Convert2Date 转换数据为日期类型
func Convert2Date(colIndex int, size int, strData string) (interface{}, bool, *ErrorInfo) {
	strData = strings.Trim(strData, " ")
	if strData == "" {
		return nil, true, nil
	}
	rs := []rune(strData)
	rsl := len(rs)
	strData2 := ""
	f := ""
	if rsl > 10 && rs[rsl-1] == 'Z' && rs[10] == 'T' {
		f = time.RFC3339
		strData2 = strData
	}
	if f == "" {
		rs2 := make([]rune, 0, rsl+2)
		index := 0
		for i, r := range rs {
			if r == '.' || r == '/' || r == '年' || r == '月' || r == '-' || r == '日' || r == ' ' || r == ':' || r == '时' || r == '分' || r == '秒' {
				if i == 2 {
					rs2 = append(rs2, '2', '0')
				}
				if len(rs[index:i]) == 1 {
					rs2 = append(rs2, '0')
				}
				rs2 = append(rs2, rs[index:i]...)
				if r != '日' && r != '秒' {
					rs2 = append(rs2, '-')
				}
				index = i + 1
			}
		}
		if rsl > index {
			if len(rs[index:rsl]) == 1 {
				rs2 = append(rs2, '0')
			}
			rs2 = append(rs2, rs[index:rsl]...)
		}
		f = "2006-01-02"
		l := len(rs2)
		if l > 16 {
			f = "2006-01-02-15-04-05"
		} else if l == 16 {
			f = "2006-01-02-15-04"
		} else if l == 10 {
			f = "2006-01-02"
		} else if l == 8 {
			f = "20060102"
		} else if l < 8 {
			f = "200612"
		}
		strData2 = string(rs2)
	}
	data, err := time.Parse(f, strData2)
	if err == nil {
		if data.Year() <= 1970 {
			return nil, true, nil
		}
		return data, true, nil
	} else {
		errInfo := &ErrorInfo{
			ColIndex: colIndex,
			Message:  fmt.Sprintf("%s不是有效的日期！", strData),
		}
		return nil, false, errInfo
	}
}
func (d *DataImport) getConvertFunc(dt schema.DataType) (ConvertFunc, int, error) {
	switch dt {
	case schema.Bool:
		return Convert2Bool, 0, nil
	case schema.Int, schema.Uint:
		return Convert2Int, 0, nil
	case schema.Float:
		return Convert2Float, 0, nil
	case schema.String:
		return Convert2String, 0, nil
	case schema.Time:
		return Convert2Date, 0, nil
	default:
		errInfo := fmt.Sprintf("字段类型%s在导入模块中不支持！", dt)
		return nil, 0, errors.New(errInfo)
	}
}

func GetExtenName(fileName string) (string, string) {
	rs := []rune(fileName)
	index := -1
	i := len(rs) - 1
	for ; i >= 0; i-- {
		if rs[i] == '.' {
			index = i
			break
		}
	}
	start := index + 1
	end := len(rs)
	return string(rs[0:index]), string(rs[start:end])
}

var alphabet [26]string = [26]string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

func indexToColName(num int) string {
	num++
	out := ""
	count := 26
	if num/count != 0 {
		out = alphabet[num/count-1]
		if num%count == 0 {
			out = out + alphabet[num%count]
		} else {
			out = out + alphabet[num%count-1]
		}
	} else {
		out = alphabet[num-1]
	}
	return out
}

// callBackPool 调入前后的回调池
var callBackPool map[string]CallBackFunc = make(map[string]CallBackFunc, 10)

// SetBeforeCallBack 导入前的回调
func SetBeforeCallBack(importName string, cb CallBackFunc) {
	name := importName + "-before"
	callBackPool[name] = cb
}

// SetAfterCallBack 导入后的回调
func SetAfterCallBack(importName string, cb CallBackFunc) {
	name := importName + "-after"
	callBackPool[name] = cb
}

// ValidationCallBackPool 验证数据的回调池
var validationCallBackPool map[string]ValidationCallBackFunc = make(map[string]ValidationCallBackFunc, 10)

// SetValidationCallBack 设置验证时的回调
func SetValidationCallBack(importName string, cb ValidationCallBackFunc) {
	validationCallBackPool[importName] = cb
}

// GetDataMap 获取数据映射
func GetDataMap(ImportName string) (string, error) {
	var config *ImportConfig
	err := MYDB.Where("name=?", ImportName).FirstOrInit(&config).Error
	if err != nil {
		return "", err
	}
	if config == nil {
		return "", nil
	}
	return config.Config, nil
}

// GetImportInfo 获取导入信息
func GetImportInfo(fileName string) (infoMap map[string]string, err error) {
	key := "ip:" + fileName
	infoMap, err = RedisCli.HGetAll(rctx, key).Result()
	return
}
